#ifndef COMMON_CLASSLIBRARY_H_
#define COMMON_CLASSLIBRARY_H_
#include <fermat/common/manifest.h>
#include <typeinfo>
#include <memory>

extern "C"
{
	bool  fermatBuildManifest(fermat::ManifestBase* pManifest);
	void  fermatInitializeLibrary();
	void  fermatUninitializeLibrary();
} 

#define FERMAT_JOIN(X, Y) FERMAT_DO_JOIN(X, Y)
#define FERMAT_DO_JOIN(X, Y) FERMAT_DO_JOIN2(X, Y)
#define FERMAT_DO_JOIN2(X, Y) X##Y

//
// additional support for named manifests
//
#define FERMAT_DECLARE_NAMED_MANIFEST(name) \
extern "C"	\
{			\
	bool  FERMAT_JOIN(fermatBuildManifest, name)(fermat::ManifestBase* pManifest); \
}


//
// Macros to automatically implement fermatBuildManifest
//
// usage:
//
// FERMAT_BEGIN_MANIFEST(MyBaseClass)
//     FERMAT_EXPORT_CLASS(MyFirstClass)
//     FERMAT_EXPORT_CLASS(MySecondClass)
//     ...
// FERMAT_END_MANIFEST
//
#define FERMAT_BEGIN_MANIFEST_IMPL(fnName, base) \
	bool fnName(fermat::ManifestBase* pManifest_)									\
	{																				\
		typedef base _Base;															\
		typedef fermat::Manifest<_Base> _Manifest;									\
		std::string requiredType(typeid(_Manifest).name());							\
		std::string actualType(pManifest_->className());							\
		if (requiredType == actualType)												\
		{																			\
			fermat::Manifest<_Base>* pManifest = static_cast<_Manifest*>(pManifest_);


#define FERMAT_BEGIN_MANIFEST(base) \
	FERMAT_BEGIN_MANIFEST_IMPL(fermatBuildManifest, base)


#define FERMAT_BEGIN_NAMED_MANIFEST(name, base)	\
	FERMAT_DECLARE_NAMED_MANIFEST(name)			\
	FERMAT_BEGIN_MANIFEST_IMPL(FERMAT_JOIN(fermatBuildManifest, name), base)


#define FERMAT_END_MANIFEST \
			return true;	\
		}					\
		else return false;	\
	}


#define FERMAT_EXPORT_CLASS(cls) \
    pManifest->insert(fermat::Manifest<_Base>::MetaPtr(new fermat::MetaObject<cls, _Base>(#cls)));


#define FERMAT_EXPORT_INTERFACE(cls, itf) \
    pManifest->insert(fermat::Manifest<_Base>::MetaPtr(new fermat::MetaObject<cls, _Base>(itf)));


#define FERMAT_EXPORT_SINGLETON(cls) \
	pManifest->insert(fermat::Manifest<_Base>::MetaPtr(new fermat::MetaSingleton<cls, _Base>(#cls)));


#endif 
